﻿//
// This file is part of AVSReflector project
// Copyright (C) 2011 Yuan Pengfei
//

using System;
using System.Collections.Generic;
using System.Text;

namespace AVSReflector.Core
{
    public class Slice : INode
    {
        public uint slice_vertical_position;
        public uint fixed_slice_qp;
        public uint slice_qp;
        public uint slice_weighting_flag;

        public MacroBlock[] blocks;

        public string[] GetInfo()
        {
            List<string> lines = new List<string>();
            lines.Add("slice_vertical_position = " + slice_vertical_position);
            lines.Add("fixed_slice_qp = " + fixed_slice_qp);
            lines.Add("slice_qp = " + slice_qp);
            lines.Add("slice_weighting_flag = " + slice_weighting_flag);
            return lines.ToArray();
        }

        public static Slice Parse(BitStream s)
        {
            Slice ret = new Slice();
            Slice bSlice = Context.currentSlice;
            bool bEscape = s.escape;

            // Parse slice header
            ret.slice_vertical_position = s.Read(8);
            if (Context.currentSequence.vertical_size > 2800)
                ret.slice_vertical_position += s.Read(3) << 7;
            if (Context.currentPicture.fixed_picture_qp == 0)
            {
                ret.fixed_slice_qp = s.Read(1);
                ret.slice_qp = s.Read(6);
            }
            Parameters.MbIndex = Context.currentSequence.mb_width * ret.slice_vertical_position;
            if (Context.currentPicture.picture_type != 0
                && Context.currentPicture.picture_type != 4
                || Context.currentPicture.picture_structure == 0
                && Parameters.MbIndex >= Context.currentSequence.mb_width * Context.currentSequence.mb_height / 2)
            {
                ret.slice_weighting_flag = s.Read(1);
                // TODO: Add slice weighting support
                if (ret.slice_weighting_flag == 1)
                    throw new Exception("Slice weighting not supported!");
            }

            // Initialize parameters
            Parameters.predQuantCoeffMatrix = new uint[8, 8];
            if (Context.currentPicture.fixed_picture_qp == 0)
            {
                Parameters.PreviousQP = (int)ret.slice_qp;
                Parameters.FixedQP = ret.fixed_slice_qp;
            }

            // Parse macro blocks
            List<MacroBlock> l = new List<MacroBlock>();
            Context.currentSlice = ret;
            s.escape = true;
            while (true)
            {
                if(s.EndOfSlice())
                    break;
                if ((Context.currentPicture.picture_type != 0
                && Context.currentPicture.picture_type != 4
                || Context.currentPicture.picture_structure == 0
                && Parameters.MbIndex >= Context.currentSequence.mb_width * Context.currentSequence.mb_height / 2)
                && Context.currentPicture.skip_mode_flag == 1)
                {
                    uint skip = Decoder.ExpGolomb(s);
                    for (uint i = 0; i < skip; ++i)
                    {
                        ret.blocks = l.ToArray();   // TODO: This is not so good
                        l.Add(MacroBlock.Parse(s, true));
                        Parameters.MbIndex++;
                    }
                }
                if (s.EndOfSlice())
                    break;
                ret.blocks = l.ToArray();   // TODO: This is not so good
                l.Add(MacroBlock.Parse(s));
                Parameters.MbIndex++;
            }
            ret.blocks = l.ToArray();

            s.escape = bEscape;
            s.SkipToCode(0x1, 24);
            Context.currentSlice = bSlice;
            return ret;
        }
    }
}
